package com.ebupt.migu.music.schedule.service.impl;

import cn.hutool.core.io.file.FileReader;
import com.ebupt.migu.music.common.constants.StrategyConstants;
import com.ebupt.migu.music.common.util.DateUtil;
import com.ebupt.migu.music.schedule.mapper.PopupMapper;
import com.ebupt.migu.music.schedule.service.PopupService;
import com.ebupt.migu.restapi.DownloadByFtp;
import com.ebupt.migu.restapi.GroupDetailDel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Intellij IDEA.
 * User:  liuyangyang@cd.ebupt.com
 * Date:  2019/10/30
 */
@Slf4j
@Service
public class PopupServiceImpl implements PopupService {

    @Autowired
    private PopupMapper popupMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${popup.ip}")
    private String POPUP_IP;
    @Value("${popup.port}")
    private int POPUP_PORT;
    @Value("${popup.user_name}")
    private String POPUP_USER_NAME;
    @Value("${popup.password}")
    private String POPUP_PASSWORD;
    @Value("${popup.local_path}")
    private String POPUP_LOCAL_PATH;
    @Value("${popup.remote_path}")
    private String POPUP_REMOTE_PATH;
    @Value("${label.prefix}")
    private String prefix;
    @Value("${label.post_url}")
    private String postUrl;

    /**
     * 假设当前人群规模为5：
     * BQ1（人群ID） ：
     * {001，00-01，131}
     * {002，00-02，132}
     * {003，00-03，133}
     * {004，00-04，134}
     * {005，00-05，135}
     * 数据分别为uid，imei/idfa，手机号码，其中imei和idfa一个用户只存在其中一个数据，不共存
     *
     * 存储到Redis将分成四份：
     * 1、key：BQ1-uid
     *  value:001,002,003,004,005
     *
     * 2、key：BQ1-imei
     *  value:00-01,00-02,00-03,00-04,00-05
     *
     * 3、key：BQ1-phone
     *  value:131，132，133，134，135
     *
     * 4、key： BQ1
     *  value： {001，00-01，131}，{002，00-02，132}，{003，00-03，133}，{004，00-04，134}，{005，00-05，135}
     */

    /**
     * 立即弹窗
     */
    @Override
    public void popupNow() {
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
        SimpleDateFormat simpleDateFormat_file = new SimpleDateFormat(DateUtil.DATE_FORMAT_YYYYMMDD);
        // 当前时间
        String now = simpleDateFormat.format(date);
        String fileDate = simpleDateFormat_file.format(date);
        // 查询需要立即弹窗的策略列表
        List<HashMap<String, Object>> strategies = popupMapper.queryPopupNow();
        if (null == strategies || strategies.isEmpty()) {
            log.info("当前不存在需要立即弹窗的策略");
            return;
        }
        Map<String, Object> strategy = strategies.get(0);
        if (null == strategy.get("popupId")) {
            log.info("当前策略没有弹窗ID");
            return;
        }
        String popupId = strategy.get("popupId").toString();
        log.info("执行策略：{}， 弹窗ID:{}", strategy.get("strategyName"), popupId);
        // 获取策略ID更新相应状态
        String strategyId = strategy.get("strategyId").toString();
        Map<String, Object> map = new HashMap<>();
        map.put("strategyId", strategyId);
        map.put("executeStatus", StrategyConstants.EXECUTE_ONGOING);
        map.put("executeTime", now);
        map.put("executing", 2);
        // 更新
        log.info("将策略状态修改为执行中并更新执行时间");
        popupMapper.updateStrategyStatus(map);

        // 走流程
        pro(strategy);

        map.clear();
        map.put("strategyId", strategyId);
        map.put("executing", 1);
        map.put("executeStatus", StrategyConstants.EXECUTE_ENDED);
        log.info("将策略状态修改为已完成");
        popupMapper.updateStrategyStatus(map);
    }

    /**
     * 单次弹窗
     */
    @Override
    public void popupOnce() {
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
        // 当前时间
        String now = simpleDateFormat.format(date);
        SimpleDateFormat simpleDateFormat_file = new SimpleDateFormat(DateUtil.DATE_FORMAT_YYYYMMDD);
        String fileDate = simpleDateFormat_file.format(date);
        // 查询需要立即发送的短信策略列表
        List<HashMap<String, Object>> strategies = popupMapper.queryPopupOnce(now);
        if (null == strategies || strategies.isEmpty()) {
            log.info("当前不存在单次弹窗的策略");
            return;
        }
        Map<String, Object> strategy = strategies.get(0);
        if (null == strategy.get("popupId")) {
            log.info("当前策略没有弹窗ID");
            return;
        }
        log.info("执行策略：" + strategy.get("strategyName"));
        // 获取策略ID更新相应状态
        String strategyId = strategy.get("strategyId").toString();
        Map<String, Object> map = new HashMap<>();
        map.put("strategyId", strategyId);
        map.put("executeStatus", StrategyConstants.EXECUTE_ONGOING);
        map.put("executeTime", now);
        map.put("executing", 2);
        // 更新
        log.info("将策略状态修改为执行中并更新执行时间");
        popupMapper.updateStrategyStatus(map);

        // 走流程
        pro(strategy);

        map.clear();
        map.put("strategyId", strategyId);
        map.put("executing", 1);
        map.put("executeStatus", StrategyConstants.EXECUTE_ENDED);
        log.info("将策略状态修改为已完成");
        popupMapper.updateStrategyStatus(map);
    }

    /**
     * 循环弹窗
     */
    @Override
    public void popupLoop() throws ParseException {
        Date date = new Date();
        // 年月日时分秒
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
        // 年月日
        SimpleDateFormat simpleDateFormat_day = new SimpleDateFormat(DateUtil.DATE_FORMAT_YYYY_MM_DD);
        // 时分秒
        SimpleDateFormat simpleDateFormat_time = new SimpleDateFormat(DateUtil.DATE_TIME_FORMAT_HH_MI_SS);
        // 当前年月日时分秒
        String dayTime = simpleDateFormat.format(date);
        // 当前年月日
        String dayOnly = simpleDateFormat_day.format(date);
        // 当前时分秒
        String timeOnly = simpleDateFormat_time.format(date);

        // 查询需要循环发送短信的策略列表
        List<HashMap<String, Object>> strategies = popupMapper.queryPopupLoop(dayOnly);
        if (null == strategies || strategies.isEmpty()) {
            log.info("当前不存在循环弹窗的策略");
            return;
        }
        Map<String, Object> strategy = new HashMap<>();
        for (Map<String, Object> s : strategies) {
            String startTime = s.get("startTime").toString();
            String[] starts = startTime.split(" ");
            String start = starts[1];
            // 最近一次执行时间为空且执行时间小于当前时间
            if (s.get("executeTime") == null && (simpleDateFormat_time.parse(start).getTime() <= simpleDateFormat_time.parse(timeOnly).getTime())) {
                strategy = s;
                break;
            }
            String executeTime = s.get("executeTime").toString();
            String[] executes = executeTime.split(" ");
            String execute = executes[0];
            // 最近一次执行时间不为空且最后一次执行日期小于今天且执行时间小于当前时间
            if ((simpleDateFormat_day.parse(execute).getTime() < simpleDateFormat_day.parse(dayOnly).getTime()) && (simpleDateFormat_time.parse(start).getTime() <= simpleDateFormat_time.parse(timeOnly).getTime())) {
                strategy = s;
                break;
            }
        }

        if (null == strategy) {
            log.info("当前不存在循环弹窗的策略");
            return;
        }
        if (null == strategy.get("popupId")) {
            log.info("当前策略没有弹窗ID");
            return;
        }
        String popupId = strategy.get("popupId").toString();
        log.info("执行策略：" + strategy.get("strategyName"));
        // 获取策略ID更新相应状态
        String strategyId = strategy.get("strategyId").toString();
        Map<String, Object> map = new HashMap<>();
        map.put("strategyId", strategyId);
        map.put("executeStatus", StrategyConstants.EXECUTE_ONGOING);
        map.put("executeTime", dayTime);
        map.put("executing", 2);
        // 更新
        log.info("将策略状态修改为执行中并更新执行时间");
        popupMapper.updateStrategyStatus(map);

        // 走流程
        pro(strategy);

        // 策略截止日期
        String endTime = strategy.get("endTime").toString();
        map.clear();
        map.put("strategyId", strategyId);
        map.put("executing", 1);
        // 截止日期是今天，将策略状态修改成已完成。 不是今天保留进行中
        String[] ends = endTime.split(" ");
        String end = ends[0];
        if (end.equals(dayOnly)) {
            map.put("executeStatus", StrategyConstants.EXECUTE_ENDED);
            log.info("将策略状态修改为已完成");
        }
        popupMapper.updateStrategyStatus(map);
    }

    /**
     * 弹窗推送主要流程
     *
     * @param strategy
     */
    private void pro(Map<String, Object> strategy) {
        // 通过何种用户标识进行弹窗
        int popupType = Integer.parseInt(String.valueOf(strategy.get("popupType")));
        // 文件生成日期
        Date date = new Date();
        SimpleDateFormat simpleDateFormat_file = new SimpleDateFormat(DateUtil.DATE_FORMAT_YYYYMMDDHH);
        String fileDate = simpleDateFormat_file.format(date);
        // 用户群ID
        String gid = String.valueOf(strategy.get("groupId"));
        // 弹窗ID
        String popupId = String.valueOf(strategy.get("popupId"));
        // 判断用户群是否属于标签服务平台
        HashMap<String, Object> labelUserGroup = popupMapper.isLabelUserGroup(gid);
        if(null != labelUserGroup && labelUserGroup.size() > 0){
            //对接标签服务平台取人群
            File file;
            if(popupType == 1){
                file = DownloadByFtp.getLabelUserGroup(strategy,prefix+postUrl,3);
                if(null == file || file.length() == 0){
                    log.info("目标人群不存在，无法进行弹窗文件传输");
                    return;
                }
                this.writeFileToLocalPath(1, file, POPUP_LOCAL_PATH + popupId + "_" + fileDate + "_UID", strategy);
                transport(popupId + "_" + fileDate + "_UID");
            }else {
                file = DownloadByFtp.getLabelUserGroup(strategy,prefix+postUrl,2);
                if(null == file || file.length() == 0){
                    log.info("目标人群不存在，无法进行弹窗文件传输");
                    return;
                }
                this.writeFileToLocalPath(2, file, POPUP_LOCAL_PATH + popupId + "_" + fileDate + "_IMEI", strategy);
                transport(popupId + "_" + fileDate + "_IMEI");
            }
            //对接标签平台客群销毁通知
            if(file.delete()){
                GroupDetailDel.del(strategy);
            }
        } else{
            // 目标人群用户数
            int members = 0;
            if (popupType == 1) {
                members = redisTemplate.opsForSet().size(gid + "-uid").intValue();
                if (members == 0) {
                    log.info("目标人群不存在，无法进行弹窗文件传输");
                    return;
                }
                this.writeFileToLocalPath(1, gid + "-uid", POPUP_LOCAL_PATH + popupId + "_" + fileDate + "_UID", strategy);
                transport(popupId + "_" + fileDate + "_UID");
            } else {
                members = redisTemplate.opsForSet().size(gid + "-imei").intValue();
                if (members == 0) {
                    log.info("目标人群不存在，无法进行弹窗文件传输");
                    return;
                }
                this.writeFileToLocalPath(2, gid + "-imei", POPUP_LOCAL_PATH + popupId + "_" + fileDate + "_IMEI", strategy);
                transport(popupId + "_" + fileDate + "_IMEI");
            }
        }
    }

    /**
     * 剔除黑名单用户
     */
    private List<String> removeBlackList(String[] ids, int type) {
        List<String> members = new ArrayList<>();
        for (String id : ids) {
            if (type == 1) {
                List<String> uids = popupMapper.queryUids(id);
                if (null != uids && uids.size() != 0) {
                    members.addAll(popupMapper.queryUids(id));
                }
            } else {
                List<Map<String, String>> whites = popupMapper.queryRosterMembers(id);
                if (null != whites && !whites.isEmpty()) {
                    for (Map<String, String> white : whites) {
                        // 添加imei
                        if (null != white.get("imei") && !white.get("imei").equals("")) {
                            members.add(white.get("imei"));
                        }
                        // 添加idfa
                        if (null != white.get("idfa") && !white.get("idfa").equals("")) {
                            members.add(white.get("idfa"));
                        }
                    }
                }
            }
        }
        return members;
    }

    /**
     * 添加白名单用户
     * type: 1- uid; 2- 设备号
     */
    private List<String> addWhiteList(String[] ids, int type) {
        List<String> members = new ArrayList<>();
        for (String id : ids) {
            if (type == 1) {
                List<String> uids = popupMapper.queryUids(id);
                if (null != uids && uids.size() != 0) {
                    members.addAll(popupMapper.queryUids(id));
                }
            } else {
                List<Map<String, String>> whites = popupMapper.queryRosterMembers(id);
                if (null != whites && !whites.isEmpty()) {
                    for (Map<String, String> white : whites) {
                        // 添加imei
                        if (null != white.get("imei") && !white.get("imei").equals("")) {
                            members.add(white.get("imei"));
                        }
                        // 添加idfa
                        if (null != white.get("idfa") && !white.get("idfa").equals("")) {
                            members.add(white.get("idfa"));
                        }
                    }
                }
            }
        }
        return members;
    }

    /**
     * 将文件写入本地
     * type: 1-uid, 2-msisdn, 3-imei
     *
     * @param path
     */
    private void writeFileToLocalPath(int type, String key, String path, Map<String, Object> strategy) {

        // 白名单： 1-添加白名单
        String[] white = null;
        // 黑名单： 1-剔除黑名单
        String[] black = null;

        // 黑白名单用户
        List<String> BMembers = new ArrayList<>();
        List<String> WMembers = new ArrayList<>();

        if (null != strategy.get("white")) {
            // 多选白名单
            white = String.valueOf(strategy.get("white")).split(",");
        }
        if (null != strategy.get("black")) {
            // 多选黑名单
            black = String.valueOf(strategy.get("black")).split(",");
        }
        // 添加白名单用户
        if (white != null && white.length > 0) {
            log.info("添加白名单用户");
            WMembers = addWhiteList(white, type);
        }
        // 剔除黑名单用户
        if (black != null && black.length > 0) {
            log.info("剔除黑名单用户");
            BMembers = removeBlackList(black, type);
        }
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(new File(path));
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream));
            Cursor<String> cursor = redisTemplate.opsForSet().scan(key, ScanOptions.NONE);
            while (cursor.hasNext()) {
                String uid = cursor.next();
                if (BMembers != null && BMembers.contains(uid)) {
                    continue;
                }
                if (WMembers != null && WMembers.contains(uid)) {
                    continue;
                }
                bufferedWriter.write(uid);
                bufferedWriter.newLine();
            }
            // 将白名单用户写入文件中
            if (WMembers != null && WMembers.size() != 0) {
                for (String member : WMembers) {
                    bufferedWriter.write(member);
                    bufferedWriter.newLine();
                }
            }
            bufferedWriter.close();
            log.info("生成文件：{}", path);
        } catch (IOException e) {
            log.error("生成文件出错:{}", path, e);
        }
    }

    /**
     * 将文件写入本地
     * type: 1-uid, 2-msisdn, 3-imei
     * @param path
     */
    private void writeFileToLocalPath(int type, File file, String path, Map<String, Object> strategy) {

        // 白名单： 1-添加白名单
        String[] white = null;
        // 黑名单： 1-剔除黑名单
        String[] black = null;

        // 黑白名单用户
        List<String> BMembers = new ArrayList<>();
        List<String> WMembers = new ArrayList<>();

        if (null != strategy.get("white")) {
            // 多选白名单
            white = String.valueOf(strategy.get("white")).split(",");
        }
        if (null != strategy.get("black")) {
            // 多选黑名单
            black = String.valueOf(strategy.get("black")).split(",");
        }
        // 添加白名单用户
        if (white != null && white.length > 0) {
            log.info("添加白名单用户");
            WMembers = addWhiteList(white, type);
        }
        // 剔除黑名单用户
        if (black != null && black.length > 0) {
            log.info("剔除黑名单用户");
            BMembers = removeBlackList(black, type);
        }
        String str;
        try {
            BufferedReader reader = new FileReader(file).getReader();
            BufferedWriter writer = new BufferedWriter(new FileWriter(path));
            while ((str = reader.readLine()) != null){
                if (BMembers != null && BMembers.contains(str)) {
                    continue;
                }
                if (WMembers != null && WMembers.contains(str)) {
                    continue;
                }
                writer.write(str);
                writer.newLine();
            }
            // 将白名单用户写入文件中
            if (WMembers != null && WMembers.size() != 0) {
                for (String member : WMembers) {
                    writer.write(member);
                    writer.newLine();
                }
            }
            reader.close();
            writer.close();
            log.info("生成文件：{}", path);
        } catch (IOException e) {
            log.error("生成文件出错:{}", path, e);
        }
    }

    /**
     * 文件传输
     *
     * @param filename
     */
    private void transport(String filename) {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.setUseEPSVwithIPv4(true);
            // 连接服务器
            ftpClient.connect(POPUP_IP, POPUP_PORT);
            log.info("IP:{}, PORT:{}", POPUP_IP, POPUP_PORT);
            // 登录ftp
            ftpClient.login(POPUP_USER_NAME, POPUP_PASSWORD);
            log.info("USERNAME:{}, PASSWORD:{}", POPUP_USER_NAME, POPUP_PASSWORD);
            // 设置编码格式
            ftpClient.setControlEncoding("UTF-8");
            // 启动被动模式
            ftpClient.enterLocalPassiveMode();
            // 需要上传的文件
            InputStream inputStream = new FileInputStream(new File(POPUP_LOCAL_PATH + filename));
            // 指定写入的目录,远程路径不存在则创建
            boolean isChanged = ftpClient.changeWorkingDirectory(POPUP_REMOTE_PATH);
            log.info("切换到指定路径:{}，切换结果:{}", POPUP_REMOTE_PATH, isChanged);
            if (isChanged) {
                boolean result = ftpClient.storeFile(new String(filename.getBytes("UTF-8"), "UTF-8"), inputStream);
                log.info("传送文件结果:{}, 文件名:{}", result, filename);
            }
            inputStream.close();
            ftpClient.logout();
        } catch (Exception e) {
            log.error("FTP异常", e);
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    log.info("关闭FTP连接");
                    ftpClient.disconnect();
                } catch (IOException e) {
                    log.error("FTP连接关闭异常", e);
                }
            }
        }
    }

}
